package com.asura.ops.sync.sdk.handle;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.asura.ops.sync.sdk.model.MqPayload;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author: zouyang
 * @date: 2022/9/12 2:32
 * @description:
 */
@Slf4j
public abstract class JdbcTemplateAbstractSyncHandler<T> implements SyncHandler<Object> {

    private static String TABLE = "$table$";
    private static String UPDATE_REPLACE_DATA = "$replaceData$";
    private static String UPDATE = "UPDATE " + TABLE + " SET " + UPDATE_REPLACE_DATA + " WHERE id=?";
    private static String INSERT_COLUMN = "$COLUMNS$";
    private static String INSERT_VAL = "$INSERT_VAL$";
    private static String DELETE = "DELETE FROM " + TABLE + " WHERE id=?";
    private static String INSERT = "INSERT INTO " + TABLE + "(" + INSERT_COLUMN + ") VALUES(" + INSERT_VAL + ")";

    private static String INSERT_OR_UPDATE = "REPLACE INTO " + TABLE + "(" + INSERT_COLUMN + ") VALUES(" + INSERT_VAL + ")";

    @Resource
    private ProcessCycle processCycle;

    //临时解决驼峰问题，  <表名,[驼峰字段]>
    protected Map<String, List<String>> tempFieldMap = Maps.newConcurrentMap();

    //表，实体映射， <mdm_sku, MdmSkuEntity>
    protected Map<String, Class> tableEntityClassMap = Maps.newConcurrentMap();

    /**
     * 源表表名 和 目标表名 映射
     */
    protected Map<String, String> sourceTargetTableMap = Maps.newConcurrentMap();


    //写入目标数据库链接
    protected List<DataSource> dataSourceList = Lists.newArrayList();

    //连接池使用
//    protected List<JdbcTemplate> jdbcTemplateList = Lists.newArrayList();

    //连接池使用  <dbname,JdbcTemplate>
    protected Map<String, JdbcTemplate> jdbcTemplateMap = Maps.newConcurrentMap();

    protected Map<Long, Boolean> handlingMap = Maps.newConcurrentMap();

    protected Map<Long, Long> timelineMap = Maps.newConcurrentMap();


    public void configTableEntityClassMap(Map<String, Class> map) {
        this.tableEntityClassMap = map;
    }

    @Override
    public void configDatasourceList(List<DataSource> dataSourceList) {
        this.dataSourceList = dataSourceList;
        for (DataSource dataSource : dataSourceList) {
            try {
                String url = dataSource.getConnection().getMetaData().getURL().split("\\?")[0];
                int index = url.lastIndexOf("/");
                String dbName = url.substring(index + 1);

                JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
                jdbcTemplateMap.put(dbName, jdbcTemplate);
            } catch (SQLException e) {
                log.error("解析数据库名称异常:{}", e);
                System.exit(100);
            }
        }

        log.info("20221206---------------------------------------");
    }

    public void configSourceTargetTableMap(Map<String, String> tableMap) {
        this.sourceTargetTableMap = tableMap;
    }

    public void configTempFieldMap(Map<String, List<String>> tempFieldMap) {
        this.tempFieldMap = tempFieldMap;
    }

    @Override
    public void handle(MqPayload payload) throws Exception {
        Class cls = tableEntityClassMap.get(payload.getSource().getTable());

        if (cls == null) {
            log.info("无对应实体，不处理");
            return;
        }

        switch (payload.getChangeType()) {
            case 1:
            case 2:
            case 4:
                processCycle.beforeHandler(getTargetTableName(payload.getSource().getTable()), cls, payload.getChangeType(), payload);
                insertOrUpdateV2(getTargetTableName(payload.getSource().getTable()), cls, payload);
                processCycle.afterHandler(getTargetTableName(payload.getSource().getTable()), cls, payload.getChangeType(), payload);
                break;
//            case 2:
//                processCycle.beforeHandler(getTargetTableName(payload.getSource().getTable()), cls, payload.getChangeType(), payload);
//
//                update(getTargetTableName(payload.getSource().getTable()), cls, payload);
//
//                processCycle.afterHandler(getTargetTableName(payload.getSource().getTable()), cls, payload.getChangeType(), payload);
//                break;
            case 3:
                processCycle.afterHandler(getTargetTableName(payload.getSource().getTable()), cls, payload.getChangeType(), payload);

                removeById(getTargetTableName(payload.getSource().getTable()), cls, payload);

                processCycle.afterHandler(getTargetTableName(payload.getSource().getTable()), cls, payload.getChangeType(), payload);

                break;
        }
    }

    @Override
    public void update(String tableName, Class clazz, Object t) throws SQLException {
        if (t == null) {
            return;
        }
        MqPayload payload = (MqPayload) t;

        Long id = Long.valueOf(payload.getAfter().get("id").toString());
        while (handlingMap.keySet().contains(id)) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                log.error("睡眠异常:{}", e);
            }
        }

        handlingMap.put(id, true);

        Set<String> clientFieldList = Sets.newConcurrentHashSet();
        for (String upper : ReflectUtil.getFieldMap(clazz).keySet()) {
            clientFieldList.add(camelToUnderline(upper, 1));
        }

        Set<String> keySet = payload.getAfter().keySet();
        List<String> fields = Lists.newArrayList();
        List<Object> values = Lists.newArrayList();
        for (String field : keySet) {
            if (field.equals("id")) {
                continue;
            }
            if (clientFieldList.contains(field)) {
                fields.add(field);
                values.add(payload.getAfter().get(field));
            }
            //解决驼峰
            if (tempFieldMap.containsKey(tableName)) {
                if (tempFieldMap.get(tableName).contains(field)) {
                    fields.add(camelToUnderline(field, 1));
                    values.add(payload.getAfter().get(field));
                }
            }
        }

        String join = StrUtil.join("=?,", fields);
        String replaceData = join + "=?";
        String updateSql = UPDATE.replace(TABLE, tableName).replace(UPDATE_REPLACE_DATA, replaceData);

//        log.info("updateSql:{}", updateSql);

        values.add(payload.getAfter().get("id"));

        this.runSql(updateSql, values, payload);

        handlingMap.remove(id);
    }

    @Override
    public void removeById(String tableName, Class<Object> clazz, Object t) throws SQLException {
        MqPayload payload = (MqPayload) t;
        Object idValue = Long.valueOf(payload.getBefore().get("id").toString());
        List<Object> values = Lists.newArrayList(idValue);

        String deleteSql = "delete from " + tableName + " where id = ?";

        log.info("deleteSql:{},{}", deleteSql, idValue);

        this.runSql(deleteSql, values, payload);

    }

    public void insertOrUpdateV2(String tableName, Class clazz, Object t) throws SQLException {
        if (t == null) {
            return;
        }
        try {
            MqPayload payload = (MqPayload) t;
            Long id = Long.valueOf(payload.getAfter().get("id").toString());

            while (handlingMap.containsKey(id)) {
                try {
                    log.info("循环等待上一个任务执行完成:{}", id);
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    log.error("睡眠异常:{}", e);
                }
            }

            if (timelineMap.containsKey(id) && timelineMap.get(id) > payload.getTs_ms()) {
                log.info("后面时间点任务已经执行了：{}", id);
                return;
            }

            if (payload.getChangeType() == 1 || payload.getChangeType() == 2) {
                handlingMap.put(id, true);
                timelineMap.put(id, payload.getTs_ms());
            }

            Set<String> clientFieldList = Sets.newConcurrentHashSet();
            for (String upper : ReflectUtil.getFieldMap(clazz).keySet()) {
                clientFieldList.add(camelToUnderline(upper, 1));
            }
            Set<String> keySet = payload.getAfter().keySet();
            List<String> fields = Lists.newArrayList();
            List<Object> values = Lists.newArrayList();
            for (String field : keySet) {
                if (clientFieldList.contains(field)) {
                    fields.add(field);
                    values.add(payload.getAfter().get(field));
                }
                //解决驼峰
                if (tempFieldMap.containsKey(tableName)) {
                    if (tempFieldMap.get(tableName).contains(field)) {
                        fields.add(camelToUnderline(field, 1));
                        values.add(payload.getAfter().get(field));
                    }
                }
            }
            String insertColumn = StrUtil.join(",", fields);
            String insertVals = StrUtil.repeatAndJoin("?", fields.size(), ",");
            String insertSql = INSERT_OR_UPDATE.replace(TABLE, tableName).replace(INSERT_COLUMN, insertColumn).replace(INSERT_VAL, insertVals);

//        log.info("insert_or_update:{}", insertSql);

            this.runSql(insertSql, values, payload);

            if (payload.getChangeType() == 1 || payload.getChangeType() == 2) {
                handlingMap.remove(id);
                if (timelineMap.size() > 10000) {
                    log.info("清理timelineMap");
                    timelineMap.clear();
                }
            }
        } catch (Exception ex) {
            log.error("消费者执行异常:{}", ex);
        }

    }

    private void runSql(String sql, List<Object> values, MqPayload payload) {
        for (String dbName : jdbcTemplateMap.keySet()) {
            if (!StringUtils.isEmpty(payload.getAppointDbName())) {
                if (dbName.equals(payload.getAppointDbName())) {
                    JdbcTemplate jdbcTemplate = jdbcTemplateMap.get(dbName);
                    jdbcTemplate.update(sql, values.toArray());
                }
            } else {
                JdbcTemplate jdbcTemplate = jdbcTemplateMap.get(dbName);
                jdbcTemplate.update(sql, values.toArray());
            }
        }
    }

    /**
     * 获取目标表名
     *
     * @param sourceTableName
     * @return
     */
    private String getTargetTableName(String sourceTableName) {
        if (CollectionUtils.isEmpty(sourceTargetTableMap)) {
            return sourceTableName;
        }
        if (sourceTargetTableMap.containsKey(sourceTableName)) {
            return sourceTargetTableMap.get(sourceTableName);
        }
        return sourceTableName;
    }

    //驼峰转下划线
    public static String camelToUnderline(String param, Integer charType) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append("_");
            }
            if (charType == 2) {
                sb.append(Character.toUpperCase(c));  //统一都转大写
            } else {
                sb.append(Character.toLowerCase(c));  //统一都转小写
            }


        }
        return sb.toString();
    }
}
